import { Tools } from './../../tools/tools';
import {
  AfterViewInit,
  ChangeDetectorRef,
  Component,
  ComponentFactoryResolver,
  ComponentRef,
  ElementRef,
  OnDestroy,
  OnInit,
  Type,
  ViewChild,
  ViewContainerRef
} from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { ExceptionHandler } from 'src/app/handler/exception.handler';
import { Condition, DataRegister, Page, PageTmpItem, Template } from 'src/app/models';
import { TemplateService, PageService, DataRegisterService } from '../../../services';
import { ZeprideTemplateRenderService } from '../../zepride-template/zepride-template.service';
import { OneColumnComponent } from '../page-layouts/one-column/one-column.component';
import { PageLayoutCommonComponent } from '../page-layouts/page-layout-common.component';
import { SudokuComponent } from '../page-layouts/sudoku/sudoku.component';
import { getDataModel } from '../page-render-tool';
import { NzMessageService } from 'ng-zorro-antd/message';
import { ConditionService } from '../../../common/services';
import { TemplateLayoutComponent } from '../../zepride-template/template-layout/template-layout.component';
import { Subscription } from 'rxjs';
import { EngineService } from '../../engines/services/engine.service';
import { EventManagerService } from 'src/app/services/event-manager.service';
import { Location } from '@angular/common';
import { AuthorityService } from '../../authority/authority.service';

@Component({
  selector: 'zepride-page-container',
  templateUrl: './page-container.component.html',
  styleUrls: ['./page-container.component.less']
})
export class PageContainerComponent implements OnInit, AfterViewInit, OnDestroy {

  templates: Template[];
  @ViewChild('containerRef', { read: ViewContainerRef }) containerRef: ViewContainerRef;
  layoutComponentRef: ComponentRef<PageLayoutCommonComponent>;
  pageId = '';
  page: Page;
  /**
   * 模板数据模型
   */
  get dataModel() {
    return this.enginerService.currentDataModel;
  }

  dataRegisterId = Tools.GetGuid();
  conditionChange$: Subscription;

  fileMakerMakeEventId = '';
  fileMakeOption: {
    showFilePerview?: boolean;
    fileInfo?: {
      fileName: string;
      url?: string;
    }
  } = {};

  menuSelectChangeSubscription: Subscription;
  isfullscreen = false;
  fsStyle: any = {
    display: this.fileMakeOption.showFilePerview ? 'flex' : 'block'
  };
  mode: 'edit' | 'view' = 'edit';
  @ViewChild('contentPageArea') set contentPageArea(ref: ElementRef<HTMLDivElement>) {
    this.contentPageAreaRef = ref;
  }
  contentPageAreaRef: ElementRef<HTMLDivElement>;
  @ViewChild('fileIframe') set fileIframe(ref: ElementRef<HTMLIFrameElement>) {
    this.fileIframeRef = ref;
  }
  fileIframeRef: ElementRef<HTMLIFrameElement>;

  constructor(
    private tplService: TemplateService,
    private pageService: PageService,
    private activatedRoute: ActivatedRoute,
    private tplRenderService: ZeprideTemplateRenderService,
    private componentFactoryResolver: ComponentFactoryResolver,
    private changeRef: ChangeDetectorRef,
    private dataRegisterService: DataRegisterService,
    private message: NzMessageService,
    private conditionService: ConditionService,
    private enginerService: EngineService,
    private eventSvr: EventManagerService,
    private router: Router,
    private location: Location,
    public authorityService: AuthorityService
  ) { }

  ngOnInit() {
    this.activatedRoute.queryParams.subscribe(data => {
      this.mode = data.mode ? data.mode : 'edit';
    });
  }

  ngAfterViewInit() {
    this.initPageData();
  }

  registFileMakerEvent() {
    this.fileMakerMakeEventId = this.eventSvr.registEvent('fileMaker.make', (data) => {
      this.fileMakeOption.showFilePerview = true;
      this.fileMakeOption.fileInfo = data;
      this.fsStyle.display = 'flex';
      setTimeout(async () => {
        const url = data.fileUrl;
        let fileType = 'word';
        let fcode = 4;
        if (data.fileName.indexOf('doc') > -1) {
          fileType = 'word';
        } else if (data.fileName.indexOf('xls') > -1) {
          fileType = 'excel';
        } else if (data.fileName.indexOf('pdf') > -1) {
          fcode = 3;
        }
        const r: any = await this.pageService.makeFile(this.dataModel, url, fcode, fileType);
        this.fileMakeOption.fileInfo.url = r.data;
        if (this.fileIframeRef && this.fileIframeRef.nativeElement) {
          this.fileIframeRef.nativeElement.src = `https://view.officeapps.live.com/op/view.aspx?src=${r.data}`;
        }
      }, 1000);
    });
  }

  initPageData() {
    this.activatedRoute.paramMap.subscribe(async paramMap => {
      try {
        this.clearValidData();
        this.enginerService.currentDataModel = null;
        this.fileMakeOption = {};
        this.pageId = paramMap.get('pageId');
        const dataRegisterId = this.activatedRoute.snapshot.queryParamMap.get('dataRegisterId');
        if (!this.dataRegisterId) {
          this.dataRegisterId = Tools.GetGuid();
        }
        const page = await this.getPageById(this.pageId);
        this.page = page;
        this.pageService.currentPage = page;
        await this.generateDataModel(this.page.pageTmpItems, dataRegisterId);
        this.layoutComponentRef = this.renderLayoutContainer();
        this.initLayoutContainerEvent(this.layoutComponentRef);
        this.changeRef.detectChanges();
        this.layoutComponentRef.instance.page = this.page;
        this.setConditions();
        this.registFileMakerEvent();
      } catch (error) {
        ExceptionHandler.current.handleError(error);
      }
    });
  }

  // handlePageNotExistError(error: HttpErrorResponse) {
  //   if (error && error.error && error.error.errorCode === HttpErrorCode.PageNotExist) {

  //   }
  // }

  getTemplates() {
    return this.tplService.getTemplates();
  }

  getPageById(pageId: string) {
    return this.pageService.getPageById(pageId);
  }

  /**
   * 汇总当前模板的条件
   */
  setConditions() {
    if (Array.isArray(this.page.pageTmpItems) && this.page.pageTmpItems.length) {
      const items = [];
      this.page.pageTmpItems.forEach(tpl => {
        if (tpl.template.condition && tpl.template.condition.sentence) {
          items.push(tpl.template.condition);
        }
        if (tpl.template.tmpComItems) {
          for (const tmpComItem of tpl.template.tmpComItems) {
            if (tmpComItem.condition) {
              items.push(tmpComItem.condition);
            }
          }
        }
      });
      this.conditionService.conditions = items;
    }
  }
  /**
   * 动态渲染模板
   * @param templateId 模板的id
   * @param container 容器
   */
  renderTemplate(templateId: string, container: HTMLDivElement) {
    const tempIndex = this.page.pageTmpItems.findIndex(temp => temp.templateId === templateId);
    if (tempIndex === -1) {
      return;
    }
    const templateData = this.page.pageTmpItems[tempIndex];
    let tplInvalid = false;
    if (templateData.template.condition) {
      const isValid = this.conditionService.checkCondition(this.dataModel, templateData.template.condition);
      if (!isValid) {
        container.style.display = 'none';
        tplInvalid = true;
      }
    }
    container.dataset.templateId = templateId;
    const componentRef = this.tplRenderService.renderTemplateToDom(container);
    templateData.template.tmpComItems.sort((prev, next) => {
      const prevIndex = prev.index ? prev.index : 0;
      const nextIndex = next.index ? next.index : 0;
      return prevIndex - nextIndex;
    });
    const initData: PageTmpItem = {
      templateId,
      ...templateData,
      dataModel: this.dataModel,
      tplInvalid,
      mode: this.mode
    };

    componentRef.instance.pageTmpItem = initData;
    componentRef.instance.config = { showBtns: false };
    container.dataset.tplname = templateData.template.name;
    this.onTemplateConditionEvent(componentRef, container);
    return componentRef;
  }
  /**
   * 页面布局类型
   * @param layoutName 布局类型
   */
  getLayoutComponent(layoutName: string) {
    layoutName = layoutName ? layoutName : 'one-column';
    let comp: Type<any>;
    switch (layoutName) {
      case 'sudoku':
        comp = SudokuComponent;
        break;
      case 'one-column':
        comp = OneColumnComponent;
        break;
    }
    return comp;
  }
  /**
   * 渲染容器
   */
  renderLayoutContainer(): ComponentRef<PageLayoutCommonComponent> {
    const component = this.getLayoutComponent(this.page.layout);
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(component);
    const viewContainerRef = this.containerRef;
    viewContainerRef.clear();
    const componentRef = viewContainerRef.createComponent(componentFactory);
    return componentRef;
  }
  /**
   * 初始化容器事件
   * @param componentRef 容器的引用
   */
  initLayoutContainerEvent(componentRef: ComponentRef<PageLayoutCommonComponent>) {
    componentRef.instance.renderTemplate = this.renderTemplate.bind(this);
  }

  /**
   * 生成页面的数据模型
   * @param template 模板
   */
  async generateDataModel(templateItems: PageTmpItem[], dataRegisterId?: string) {
    if (dataRegisterId) {
      const data = await this.dataRegisterService.getDataRegister(dataRegisterId);
      this.enginerService.currentDataModel = data.data.fields;
      this.enginerService.currentDataModel.id = data.id;
    } else {
      templateItems.forEach(tpl => {
        const data = getDataModel(tpl);
        this.enginerService.currentDataModel = {
          ...this.dataModel,
          ...data
        };
      });
    }
    this.dataRegisterService.tempDataRegisterId = null;
  }
  /**
   * 保存当前页面数据
   */
  async save() {
    try {
      const valid = await this.validateComponents();
      if (!valid) {
        return;
      }
      const dataType = this.getDataType();
      const dataRegister: DataRegister = {
        id: this.dataModel && this.dataModel.id ? this.dataModel.id : this.dataRegisterId,
        dataType,
        data: {
          fields: {
            ...this.dataModel
          }
        },
        objectId: this.pageId,
        objectType: 'page'
      };
      await this.dataRegisterService.createDataRegister(dataRegister, []);
      this.message.success('保存成功');

      if (this.page.refPageId) {
        this.ngOnDestroy();
        this.router.navigate(['home/page/normal-page', this.page.refPageId]);
      } else {
        location.reload();
      }
    } catch (error) {
      ExceptionHandler.current.handleError(error);
    }
  }

  back() {
    this.ngOnDestroy();
    this.router.navigate(['home/page/normal-page', this.page.refPageId]);
  }

  pageBack() {
    if (this.page && this.page.refPageId) {
      this.back();
    } else {
      this.location.back();
    }
  }
  /**
   * 校验当前页面组件的数据
   */
  async validateComponents() {
    const params = {
      dataType: this.getDataType(),
      objectType: 'page' as any
    };
    const result = await this.enginerService.validateComponents(params);
    if (result && result.length) {
      const msg = result.map(x => x.errorMsg).filter(x => x).join(',');
      this.message.error(`验证失败${msg ? '【' + msg + '】' : ''}，请检查表单！`);
      return false;
    } else {
      return true;
    }
  }

  getDataType() {
    const url = this.page.submitUrl;
    const params = url.split('?')[1];
    if (params) {
      const result = params.split('&');
      const target = result.find(str => str.indexOf('dataType=') > -1);
      if (target) {
        return target.split('=')[1];
      }
    }
    return '';
  }
  /**
   * 数据变化时，判断模板应该显示还是隐藏
   */
  onTemplateConditionEvent(componentRef: ComponentRef<TemplateLayoutComponent>, tplContainer: HTMLDivElement) {
    this.conditionChange$ = componentRef.instance.conditionChangeEvent.subscribe((condition: Condition) => {
      if (condition) {
        if (condition.result) {
          tplContainer.style.display = 'block';
          componentRef.instance.componentRefs.forEach(com => {
            com.instance.componentData.tplInvalid = false;
          });
        } else {
          tplContainer.style.display = 'none';
          componentRef.instance.componentRefs.forEach(com => {
            com.instance.componentData.tplInvalid = true;
          });
        }
      }
    });
  }

  clearValidData() {
    this.conditionService.clear();
    if (this.conditionChange$) {
      this.conditionChange$.unsubscribe();
    }
    this.enginerService.clear();
  }

  exitFileMake() {
    this.fileMakeOption.showFilePerview = false;
    this.fsStyle.display = 'block';
    this.eventSvr.callEvents('fileMaker.make.cancel', {});
  }

  toggleFullScreen() {
    const isFullscreen = document.fullscreenElement;
    const element = this.contentPageAreaRef;
    if (element && element.nativeElement) {
      if (isFullscreen) {
        document.exitFullscreen();
        this.fsStyle.backgroundColor = 'none';
        this.fsStyle.overflow = 'hidden';
        this.fsStyle.display = 'block';
      } else {
        element.nativeElement.requestFullscreen();
        this.fsStyle.backgroundColor = '#f0f2f5';
        this.fsStyle.overflow = 'auto';
        this.fsStyle.display = 'flex';
      }
    }
  }

  downloadFile() {
    if (this.fileMakeOption.fileInfo.url) {
      window.open(this.fileMakeOption.fileInfo.url);
    }
  }

  ngOnDestroy() {
    this.clearValidData();
    this.dataRegisterId = null;
    this.eventSvr.removeEvent(this.fileMakerMakeEventId);
  }
}
